在每一次請求api都要帶上token讓後端來驗證這個請求是否是有經過認證的,若沒有驗證過就不能讓它去訪問api取得或是更改資料,請求圖如下。
JWT(JSON Web Token)是一個網路開放標準(RFC 7519)是無狀態性的,通常利用JWT來對使用者進行身分驗證。
JWT 是一組字串,透過(.)切分成三個為 Base64 編碼的部分:
Signature 是這樣組成
HMACSHA256(
base64(header) + "." +
base64(payload),
secret)
JWT的簽發與驗證都須使用這個 secret key,當其他人得知這個 secret,那就意味著我們可以自己簽發JWT ,因此在任何情況都不應該外流。
前端存取api時必須在header帶上Authorization: Basic xxxxxx
到後端進行身分驗證
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
JwtTokenUtils.java
@Component
public class JwtToken implements Serializable {
private static final long EXPIRATION_TIME = 1 * 60 * 1000;
/**
* JWT SECRET KEY
*/
private static final String SECRET = "learn to dance in the rain";
/**
* 簽發JWT
*/
public String generateToken(HashMap<String, String> userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put( "userName", userDetails.get("userName") );
return Jwts.builder()
.setClaims( claims )
.setExpiration( new Date( Instant.now().toEpochMilli() + EXPIRATION_TIME ) )
.signWith( SignatureAlgorithm.HS512, SECRET )
.compact();
}
/**
* 驗證JWT
*/
public void validateToken(String token) throws AuthException {
try {
Jwts.parser()
.setSigningKey( SECRET )
.parseClaimsJws( token );
} catch (SignatureException e) {
throw new AuthException("Invalid JWT signature.");
}
catch (MalformedJwtException e) {
throw new AuthException("Invalid JWT token.");
}
catch (ExpiredJwtException e) {
throw new AuthException("Expired JWT token");
}
catch (UnsupportedJwtException e) {
throw new AuthException("Unsupported JWT token");
}
catch (IllegalArgumentException e) {
throw new AuthException("JWT token compact of handler are invalid");
}
}
}
[POST]/api/login
,接收使用者登入@RestController
@RequestMapping("/api")
public class TodoController {
@PostMapping("/login")
public ResponseEntity login(@RequestBody HashMap <String, String> user) {
JwtToken jwtToken = new JwtToken();
String token = jwtToken.generateToken(user); // 取得token
return ResponseEntity.status(HttpStatus.OK).body(token);
}
}
用POSTMAN串接[POST]/api/login
會回傳一組token
可以將token放置jwt.io/ 裡去解碼出來看看token藏了哪些資訊
假設我們現在存取某一支api [GET]/api/hello
,要在header中帶入Authorization Basic tokenxxxxx
,並回傳結果。
@RestController
@RequestMapping("/api")
public class TodoController {
@GetMapping("/hello")
public ResponseEntity hello(@RequestHeader("Authorization") String au) {
String token = au.substring(6);
JwtToken jwtToken = new JwtToken();
try {
jwtToken.validateToken(token);
} catch (AuthException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
}
return ResponseEntity.status(HttpStatus.OK).body("Hello CaiLi");
}
}
接著用POSTMAN存取[GET]/api/hello
,回傳status code 200 OK
因為token 的過期時間為一分鐘,一分鐘後再請求一次,則會回傳403 Forbidden
JWT(JSON Web Token) — 原理介紹
Spring Boot Security 整合JWT實現無狀態的分散式API介面